home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / Telnet 2.6.1d1 4⁄26⁄94 Folder / source / test / Audit.c next >
C/C++ Source or Header  |  1994-04-16  |  34KB  |  1,100 lines

  1. #include "debug.h"
  2. #ifdef SUPPORT_AUDIT
  3. /*                                        Audit.c                                    */
  4. #define PARANOIA    0
  5. #include <Packages.h>
  6. /*
  7.  * Audit.c
  8.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  9.  * Programmed by Martin Minow,
  10.  *    Internet:    minow@apple.com
  11.  *    AppleLink:    MINOW
  12.  * Version of January 14, 1993
  13.  *
  14.  * Edit History
  15.  *    93.01.09 MM        First public release
  16.  *    93.01.14 MM        Think and MPW generate different record sizes; a disaster if
  17.  *                    you create an Audit Record under Think and call Audit compiled
  18.  *                    under MPW. Also added a test for record sizes and included
  19.  *                    record size information in the AuditRecord.
  20.  *    93.01.21 MM        Fix bug in procedure name recognizer that failed for certain
  21.  *                    pascal functions.
  22.  *    93.06.10 MM        Cosmetic edits for Think C 6.0. No functional changes. Text
  23.  *                    was reformatted to fit into an "80-column" line.
  24.  *    93.08.14 MM        Added "logicalRAMSize to the AuditRecord. This protects
  25.  *                    Audit (somewhat) against bad data forcing address errors.
  26.  *
  27.  * This library implements a drop-in logging capability for device drivers,
  28.  * callback functions, applications, and all other Macintosh code segments.
  29.  *
  30.  * The overall philosophy is that an application or driver allocates a record in
  31.  * the system heap that contains a small preamble and one or more 48 (decimal)
  32.  * byte blocks of data. The driver logs data to the log area, where each log
  33.  * record contains an identification value and up to four longwords of data.
  34.  *
  35.  * The user would then write a log control application that can turn logging on or
  36.  * off and dump/format the data in the log. The user can also use a MacsBug dcmd
  37.  * to display the current state of the log area.
  38.  *
  39.  * To add logging to a driver, add the following variable to the driver control
  40.  * record:
  41.  *        AuditPtr            auditPtr;
  42.  * and initialize the driver log (in the driver open routine) by calling
  43.  *        devCtlEnt->auditPtr = InitAudit(
  44.  *                        gestaltSelector,
  45.  *                        nEntries,
  46.  *                        initiallyEnabled,
  47.  *                        preserveFirstFlag
  48.  *                    );
  49.  *
  50.  * The intialization routine creates an audit record in the System Heap and
  51.  * defines a gestalt selector with a pointer to the audit area. Once created,
  52.  * the record remains in the heap until the Macintosh is rebooted. The
  53.  * gestaltSelector should be unique to the driver. InitAudit need only be
  54.  * called once by any code segment that can allocate memory (i.e., by an
  55.  * application, INIT, or driver open function). Note that you do not
  56.  * need to check errors: if InitAudit fails to create (or locate) the record,
  57.  * it returns NULL and all other routines, upon receiving a NULL AuditPtr
  58.  * argument, do nothing gracefully.
  59.  *
  60.  * The log record contains a fixed header with the following information:
  61.  *
  62.  *    version.u.low        The earliest version of the AuditRecord that this instance
  63.  *                        of the library understands.
  64.  *    version.u.high        The latest version of the AuditRecord that this instance
  65.  *                        of the library understands.
  66.  *    size.auditRecord    The size of the Audit Record (excluding the dummy first
  67.  *                        entry). This protects against compiler alignment errors
  68.  *                        (which may occur if an Audit Record is created by code
  69.  *                        generated by one compiler, and accessed by code generated
  70.  *                        by a different compiler.
  71.  *    size.auditEntry        The size of the AuditQueueEntry. (See above.)
  72.  *    free.Queue            A standard O.S. queue with available data logging records.
  73.  *    data.queue            A standard O.S. queue with records waiting to be displayed
  74.  *                        or written to a file.
  75.  *    lostData            This counter is incremented when Audit function cannot
  76.  *                        obtain a record from the free queue.
  77.  *    PSN                    Awaken this process when something is stored.
  78.  *    refNum                a void * (longword) that may be used by the calling
  79.  *                        software.
  80.  *    isLogging            TRUE if logging is enabled.
  81.  *    timeAtStart            GetDateTime() when the log was created.
  82.  *    ticksAtStart        TickCount() when the log was created.
  83.  *    entries[]            A vector of AuditEntry records.
  84.  *
  85.  * The audit user accesses the log record by calling functions: it should not
  86.  * access the structure directly.
  87.  *
  88.  * Each log record is stored in a AuditEntry structure that contains the
  89.  * following information:
  90.  *    tickCount            The Ticks value at the time the data was collected.
  91.  *    lostData            This is the number of records that were "lost" before this
  92.  *                        record because there was no free entry in the queue.
  93.  *    idCode                This longword identifies the log entry (i.e. who logged it).
  94.  *    format                This longword describes the format of the log data.
  95.  *    data                This 32 byte data area contains the log. If format is zero,
  96.  *                        the data is a Str31. Otherwise, it contains up to 8
  97.  *                        (sizeof data / sizeof (long)) longwords of data.
  98.  *
  99.  * Your application or driver calls the following functions to manage the log:
  100.  *
  101.  *        AuditPtr            InitAudit(
  102.  *            OSType                gestaltSelector,
  103.  *            short                nEntries
  104.  *            Boolean                initiallyEnabled,
  105.  *            Boolean                preserveFirst
  106.  *        );
  107.  *
  108.  *    Create the log record and "publish" a Gestalt selector with a pointer to the
  109.  *    record. This is normally called by a driver when it is first opened.
  110.  *    gestaltSelector is unique to the driver. InitAudit returns a pointer to the
  111.  *    log record or NULL if it could not create the log. If the gestaltSelector was
  112.  *    already registered, it returns a pointer to the already-created log without
  113.  *    creating a new log.
  114.  *
  115.  *        void                Audit(
  116.  *            AuditPtr            auditPtr,
  117.  *            OSType                idCode,
  118.  *            unsigned long        format,
  119.  *            ...
  120.  *        );
  121.  *
  122.  *    Store data in the log. idCode identifies this log request: by convention, it
  123.  *    is an OSType. The format parameter simplifies display by defining the format
  124.  *    of the subsequent data, The remaining parameters are log dependent. If auditPtr
  125.  *    is NULL or logging disabled, the function does nothing, but does not fail.
  126.  *
  127.  *    The AuditFormat macro is used to specify the format of the stored data:
  128.  *
  129.  *        AuditFormat(arg1, arg2, ..., arg8)
  130.  *
  131.  *    where arg1, etc. is specified by one of the following constants:
  132.  *        kAuditFormatEnd            No more data
  133.  *        kAuditFormatSigned        The datum is a 32-bit signed decimal integer
  134.  *        kAuditFormatUnsigned    The datum is a 32-bit unsigned decimal integer
  135.  *        kAuditFormatHex            The datum is a 32-bit address or 4-byte character
  136.  *        kAuditFormatAddress        The datum is a 32-bit hex value (never characters)
  137.  *        kAuditFormatString        The datum is a Pascal string. This must be the last
  138.  *                                (or only) argument. The string will be truncated
  139.  *                                to fit the remaining space. I.e. if it is the only
  140.  *                                argument, a 31-byte (plus one byte count) string
  141.  *                                will be stored.
  142.  *        kAuditFormatLocation    This must be the last format, but there is no
  143.  *                                associated value. Instead, the library stores
  144.  *                                the calling function name (the MacsBug name).
  145.  *                                The name will be truncated as for kAuditFormatString.
  146.  *
  147.  * Note that AuditFormat1, AuditFormat2, etc. macros are provided to simplify
  148.  * access to AuditFormat().
  149.  *
  150.  * Important: all parameters to Audit must be longwords. Short values (such as
  151.  * Booleans and OSErr codes) must be cast to long or unsigned long:
  152.  *        Audit(auditPtr, 'Err!', AuditFormat1(kAuditFormatSigned),
  153.  *            (long) statusError);
  154.  * If you do not do this, the data may be logged incorrectly or, in extreme
  155.  * cases, your program may crash.
  156.  *
  157.  ***
  158.  *        Boolean                AuditRead(
  159.  *            AuditPtr            auditPtr,
  160.  *            AuditEntry            *thisAuditEntry
  161.  *        );
  162.  *    An application program calls AuditRead periodically to retrieve data from the
  163.  *    log.  If there is a log entry to process, it is copied to thisAuditEntry and
  164.  *    the function returns TRUE. If it returns FALSE, there is no data waiting. This
  165.  *    function should be called from the log application's event loop. AuditRead
  166.  *    manages all log queues. Note that reading the log resets the missedDataCount.
  167.  *
  168.  * Each log record is time-stamped by the following algorithm:
  169.  *        GetAuditStartTimes(auditPtr, &gTimeAtStart, &gTicksAtStart);
  170.  *        if (AuditRead(auditPtr, &thisAuditEntry)) {
  171.  *            elapsedTicks = thisAuditEntry.tickCount - gTicksAtStart;
  172.  *            SecsToDate(
  173.  *                gTimeAtStart + (elapsedTicks / 60),
  174.  *                &logEntryDateString
  175.  *            );
  176.  *            printf(, ..., date.second, elapsedTicks % 60);
  177.  *        }
  178.  * See AuditEntryFormat.c for details.
  179.  *
  180.  ***
  181.  *        AuditPtr            GetAuditPtr(
  182.  *            OSType                gestaltSelector
  183.  *        );
  184.  *
  185.  *    Return a pointer to the common storage area, if it exists. This returns NULL
  186.  *    if there is no log "registered" by that name.
  187.  *
  188.  ***
  189.  *        void                WakeUpAudit(
  190.  *            AuditPtr            auditPtr,
  191.  *            ProcessSerialNumber    *oldPSN
  192.  *        );
  193.  *
  194.  *    Wake up this process (application) when something is logged: called by:
  195.  *            GetCurrentProcess((&oldPSN);
  196.  *            WakeUpAudit(auditPtr, &oldPSN);
  197.  *            ... logging stuff ...
  198.  *            WakeUpAudit(auditPtr, &oldPSN);    // restore previous
  199.  *            ExitToShell();
  200.  *
  201.  ***
  202.  *        Boolean                EnableAudit(
  203.  *            AuditPtr            auditPtr,
  204.  *            Boolean                enableLogging
  205.  *        );
  206.  *
  207.  *    Enable or disable logging. This returns the previous logging value. At
  208.  *    initialization, a compile-time parameter sets the initial logging value.
  209.  *
  210.  ***
  211.  *        Boolean                IsAuditEnabled(
  212.  *            AuditPtr            auditPtr
  213.  *        );
  214.  *
  215.  *    Return TRUE if auditing is enabled for this audit record.
  216.  *
  217.  ***
  218.  *        void                GetAuditStartTimes(
  219.  *            AuditPtr            auditPtr,
  220.  *            unsigned long        *timeAtStart,
  221.  *            unsigned long        *ticksAtStart
  222.  *        );
  223.  *    Return the times that the log was created. Using these values, the log display
  224.  *    application can time-stamp all log entries.
  225.  *
  226.  ***
  227.  *        void                SetAuditRefNum(
  228.  *            AuditPtr            auditPtr,
  229.  *            void                *refNum
  230.  *        );
  231.  * Store a user-controlled reference value. This may be coerced to any scalar
  232.  *    value (such as a memory pointer or longword).
  233.  *
  234.  ***
  235.  *        void                *GetAuditRefNum(
  236.  *            AuditPtr            auditPtr
  237.  *        );
  238.  * Return the current user-controlled reference value. This may be coerced to any
  239.  * scalar value (such as a memory pointer or longword). This returns zero if
  240.  * auditPtr is NULL or no value had been stored.
  241.  */
  242.  
  243. #include "Audit.h"
  244. #include <Types.h>
  245. #include <Traps.h>
  246. #include <Errors.h>
  247. #include <Memory.h>
  248. #include <GestaltEqu.h>
  249. #include <OSUtils.h>
  250. #ifndef PARANOIA
  251. #define PARANOIA    0
  252. #endif
  253.  
  254. /*
  255.  * The version id's are defined as (majorVersion << 8) | minorVersion
  256.  */
  257. #define kAuditEarliestVersion    ((1 << 8) | 2)
  258. #define kAuditLatestVersion        ((1 << 8) | 2)
  259.  
  260. #ifndef TRUE
  261. #define TRUE    1
  262. #define FALSE    0
  263. #endif
  264. /*
  265.  * This is Think C specific, and possibly release dependent.
  266.  */
  267. #if defined(_Gestalt) == FALSE && defined(_GestaltDispatch)
  268. #define _Gestalt    _GestaltDispatch
  269. #endif
  270.  
  271. /*
  272.  * Note that this file contains 68000 assembly. This must be modified when
  273.  * the Audit Library is converted to Power PC.
  274.  */
  275. /*
  276.  * These values must match values in AuditDCMD.c
  277.  */
  278. #define kAuditMagicHeaderSize    7            /* Size in longwords            */
  279. #define kAuditMagicHeader_0        0x4E560000    /* First word in Gestalt result    */
  280.  
  281. #if 0
  282. /*
  283.  * The following sequence was used to create the GestaltSelector stub. It is
  284.  * never compiled directly, but was pasted into a temporary file which was
  285.  * decompiled to get the machine code. 
  286.  */
  287. static pascal OSErr    GestaltSelector(
  288.         OSType            selector,
  289.         long            *result
  290.     )
  291. {
  292.         asm {
  293.             /*
  294.              * 22 was determined by painful observation and many pleasant
  295.              * encounters with MacsBug.
  296.              */
  297.             lea            22(pc),a0        /* a0 = Address of auditRecord        */
  298.             movea.l        result,a1        /* a1 -> result                        */
  299.             move.l        a0,(a1)            /* Store result                        */
  300.         }
  301.         return (noErr);
  302. }
  303. #endif
  304.  
  305. /*
  306.  * DisableInterrupts and EnableInterrupts manipulate the 68000 status register.
  307.  * They are expanded inline. A comment consisting of *** in the left margin
  308.  * indicates code that is run with interrupts disabled.
  309.  *
  310.  * unsigned short DisableInterrupts(void) {
  311.  *        asm {
  312.  *            move        sr,d0
  313.  *            move        #0x2600,sr
  314.  *        }
  315.  */
  316. unsigned short DisableInterrupts(void) = { 0x40C0, 0x46FC, 0x2600 };
  317. #ifdef THINK_C
  318. /*
  319.  * Since Think C has a real inline assembler, we can save one or two instructions.
  320.  * Nerd fun.
  321.  */
  322. #define EnableInterrupts(saveSR) asm {            \
  323.             move        saveSR,sr                \
  324.         }
  325. #else
  326. /*
  327.  * Even though the saveSR argument is a short, we must declare the function as a
  328.  * long so that both Think and MPW generate the same code. Otherwise, Think pushes
  329.  * a short and 2(sp) addresses the wrong value. (Guess how I discovered this.)
  330.  * Using the Think C inline defined above also eliminates the problem.
  331.  *
  332.  * void EnableInterrupts(unsigned long saveSR) {
  333.  *        asm {
  334.  *            move        2(sp),sr
  335.  *        }
  336.  */
  337. void EnableInterrupts(unsigned long saveSR) = { 0x46EF, 0x0002 };
  338. #endif
  339.  
  340. /*
  341.  * This inline copies the value of A6 (the frame pointer) to D0. It is needed in
  342.  * order to get the caller's function name.
  343.  */
  344. unsigned long *GetA6(void) = { 0x200E };    /* move.l A6,D0    */
  345.  
  346. #define LOG        (*auditPtr)
  347. #define ENTRY    (entryPtr->theEntry)
  348.  
  349. static void                            StoreString(
  350.         const StringPtr        theString,        /* Source, length may be wrong    */
  351.         unsigned short        stringLength,    /* Purported actual length        */
  352.         unsigned short        maxLength,        /* Maximum destination length    */
  353.         StringPtr            result            /* Destination pointer            */
  354.     );
  355. static AuditQueueEntryPtr            GetQueueEntry(
  356.         QHdrPtr                    theQueue
  357.     );
  358. #define PutQueueEntry(theQueue, theEntry) do {                \
  359.         Enqueue((QElemPtr) &theEntry->qLink, theQueue);        \
  360.     } while (0)
  361.  
  362. static Boolean                        TrapAvailable(
  363.         short                    theTrap
  364.     );
  365. static void                            GetCallerName(
  366.         unsigned long            *destPtr,
  367.         unsigned short            maxString,
  368.         unsigned long            *a6
  369.     );
  370.  
  371. /*
  372.  * InitAudit
  373.  */
  374. AuditPtr
  375. InitAudit(
  376.         OSType                    gestaltSelector,
  377.         unsigned short            nEntries,
  378.         Boolean                    initiallyEnabled,
  379.         Boolean                    preserveFirst
  380.     )
  381. {
  382.         register unsigned long        *recordPtr;
  383.         register AuditPtr            auditPtr;
  384.         register AuditQueueEntryPtr    queueEntryPtr;
  385.         Size                        logRecordSize;
  386.         short                        i;
  387.         OSErr                        status;
  388.         
  389.         /*
  390.          * This will not compile on either Think or MPW if the sizes are correct.
  391.          * Unfortunately, sizeof cannot be incorporated into a #define, so we
  392.          * have to do this at execution time.
  393.          */
  394.         if (sizeof (AuditEntry) != kSizeofAuditEntry)
  395.             DebugStr("\pAuditEntry size error");
  396.         if (sizeof (AuditQueueEntry) != kSizeofAuditQueueEntry)
  397.             DebugStr("\pAuditQueueEntry size error");
  398.         if (sizeof (AuditRecord) != kSizeofAuditRecord)
  399.             DebugStr("\pAuditRecord size error");
  400.         /*
  401.          * Presuppose problems and exit if the system is so old that it doesn't
  402.          * even support Gestalt. This is slightly overkill, as the current
  403.          * Gestalt glue does the TrapAvailable check for us.
  404.          */
  405.         auditPtr = NULL;
  406.         if (TrapAvailable(_Gestalt) == FALSE)
  407.             goto exit;
  408.         logRecordSize = sizeof (AuditRecord)
  409.                     + (kAuditMagicHeaderSize * sizeof (long))
  410.                     + ((nEntries - 1) * sizeof (AuditQueueEntry));
  411. #if PARANOIA > 1
  412.         {
  413.             Str255                    foo;
  414.             NumToString(sizeof (AuditQueueEntry), foo);
  415.             DebugStr(foo);
  416.             NumToString(logRecordSize, foo);
  417.             DebugStr(foo);
  418.         }
  419. #endif
  420.         recordPtr = (unsigned long *) NewPtrSysClear(logRecordSize);
  421.         if (recordPtr == NULL)
  422.             goto exit;
  423.         auditPtr = (AuditPtr) &recordPtr[kAuditMagicHeaderSize];
  424.         /*
  425.          * The code for the Gestalt Selector function precedes the actual audit
  426.          * record. Gestalt calls this function when a program calls GetAuditPtr
  427.          * for our selector. The function returns the address of the AuditRecord
  428.          * as the selector value. This code will need revision when run on
  429.          * non-Motorola cpu's. See the definition of GestaltSelector above for
  430.          * the original code.
  431.          *
  432.          * asm {
  433.          *        link        a6,#0000    Create stack frame    0x4E56 0x0000
  434.          *        lea            22(pc),a0    a0 -> auditRecord    0x41FA 0x0016  
  435.          *        movea.l        8(a6),a1    a1 -> response        0x226E 0x0008
  436.          *        move.l        a0,(a1)        Store result        0x2288
  437.          *        clr.w        8(a6)        Result := noErr        0x426E 0x0010
  438.          *        unlk        a6            Delete stack frame    0x4E5E
  439.          *        movea.l        (a7)+,a0    Pop return address    0x205F
  440.          *        addq.l        #0x8,a7        Clear stack params    0x508F
  441.          *        jmp            (a0)        Return to caller    0x4ED0
  442.          *        dc.w        0            Fake MacsBug name    0x0000
  443.          *        ** AuditRecord starts here **
  444.          *    };
  445.          */
  446. #if kAuditMagicHeader_0 != 0x4E560000
  447.     << error, the following won't work: see AuditDCMD.c >>
  448. #endif
  449.         recordPtr[0] = 0x4E560000;
  450.         recordPtr[1] = 0x41FA0016;
  451.         recordPtr[2] = 0x226E0008;
  452.         recordPtr[3] = 0x2288426E;
  453.         recordPtr[4] = 0x00104E5E;
  454.         recordPtr[5] = 0x205F508F;
  455.         recordPtr[6] = 0x4ED00000;        /* Pad with fake MacsBug name    */
  456. #if kAuditMagicHeaderSize != 7
  457.     << error, the above won't work: see AuditDCMD.c >>
  458. #endif
  459.         /*
  460.          * If this machine has a data/instruction cache, flush it so that we
  461.          * cannot execute stale data.
  462.          */
  463.         if (TrapAvailable(_HWPriv)) {
  464.             FlushDataCache();
  465.             FlushInstructionCache();
  466.         }
  467.         /*
  468.          * Add this as a gestalt. Errors present problems: either this was already
  469.          * added (no big deal), or the luser is trying to redefine a "normal"
  470.          * Gestalt. We check by trying to get the AuditPtr and hope that
  471.          * GetAuditPtr's internal checks ferret out an attempt to redefine a
  472.          * normal Gestalt.
  473.          */
  474.         status = NewGestalt(gestaltSelector, (ProcPtr) recordPtr);
  475.         if (status == gestaltDupSelectorErr) {
  476.             /*
  477.              * Trouble! this has already been added! Your program
  478.              * may eventually crash if the gestalt is not an audit
  479.              * record: see GetAuditPtr() for the safety checks.
  480.              */
  481.             DisposPtr((Ptr) auditPtr);
  482.             auditPtr = GetAuditPtr(gestaltSelector);
  483.             goto exit;
  484.         }
  485.         else if (status != noErr) {
  486.             /*
  487.              * Something is seriously out of order. Just return NULL.
  488.              */
  489.             DisposPtr((Ptr) recordPtr);
  490.             auditPtr = NULL;
  491.             goto exit;
  492.         }
  493.         /*
  494.          * Setup the rest of the AuditRecord.
  495.          */
  496.         LOG.version.u.low = kAuditEarliestVersion;
  497.         LOG.version.u.high = kAuditLatestVersion;
  498.         LOG.recordSize = kAuditRecordSize;
  499.         /*
  500.          * Store the top memory location in the AuditRecord. This helps protect
  501.          * us from crashes in Audit if the caller passes a garbage StringPtr.
  502.          */
  503.         Gestalt(gestaltLogicalRAMSize, (long *) &LOG.logicalRAMSize);
  504.         GetDateTime(&LOG.timeAtStart);
  505.         LOG.ticksAtStart = TickCount();
  506.         LOG.PSN.lowLongOfPSN = kNoProcess;
  507.         /*
  508.          * Ok, so far. Now build the initial free queue.
  509.          */
  510.         queueEntryPtr = &LOG.entries[0];
  511.         for (i = 0; i < nEntries; i++) {
  512.             PutQueueEntry(&LOG.free.queue, queueEntryPtr);
  513.             ++queueEntryPtr;
  514.         }
  515. exit:    EnableAudit(auditPtr, initiallyEnabled);
  516.         PreserveAudit(auditPtr, preserveFirst);
  517.         return (auditPtr);
  518. }
  519.  
  520. /*
  521.  * This function logs data if logging is enabled.
  522.  *
  523.  *        auditPtr    As returned by InitAudit. If NULL, nothing is logged.
  524.  *        idCode        A user-controlled value, by convention an OSType (4-byte
  525.  *                    character), that identifies this entry. The display program
  526.  *                    prints it.
  527.  *        format        The format of the remaining data: kAuditFormatString if the
  528.  *                    datum is a pascal string, otherwise, it is as created by the
  529.  *                    AuditFormat macro.
  530.  *        ...            Additional data as needed. These values must be cast to
  531.  *                    longwords to prevent ThinkC/MPW incompatibilities.
  532.  */
  533. void
  534. Audit(
  535.         register AuditPtr        auditPtr,
  536.         OSType                    idCode,
  537.         unsigned long            format,
  538.         ...
  539.     )
  540. {
  541.         unsigned short                saveSR;
  542.         va_list                        argPtr;
  543.         register unsigned long        *destPtr;
  544.         unsigned long                tickCount;
  545.         unsigned short                maxString;
  546.         unsigned short                thisFormat;
  547.         StringPtr                    theString;
  548.         unsigned char                hexString[10];
  549.         register AuditQueueEntryPtr    entryPtr;
  550.         register AuditQueueEntryPtr    nextPtr;
  551.  
  552.         tickCount = TickCount();
  553.         if (auditPtr != NULL && (LOG.flags & kAuditEnabledMask) != 0) {
  554.             entryPtr = GetQueueEntry(&LOG.free.queue);
  555.             /*
  556.              * Note: *** in the left-most column indicates code that is executed
  557.              * with interrupts disabled.
  558.              */
  559.             saveSR = DisableInterrupts();
  560. /***/        if (entryPtr == NULL) {
  561. /***/            ++LOG.lostData;
  562. /***/            EnableInterrupts(saveSR);
  563.                 if ((LOG.flags & kAuditPreserveFirstMask) == 0) {
  564.                     /*
  565.                      * We really want to log the latest entry. Remove the first
  566.                      * "to be displayed" entry. If successful, it will get the new
  567.                      * record and the next "to be displayed" entry, if any, gets
  568.                      * the lost data counter.
  569.                      */
  570.                     entryPtr = GetQueueEntry(&LOG.data.queue);
  571.                     if (entryPtr == NULL)
  572.                         ;                /* goto return        */
  573.                     else {
  574. /***/                    saveSR = DisableInterrupts();
  575. /***/                    LOG.lostData += ENTRY.lostData;
  576. /***/                    nextPtr = (AuditQueueEntryPtr) LOG.data.queue.qHead;
  577. /***/                    if (nextPtr != NULL) {
  578. /***/                        nextPtr->theEntry.lostData += LOG.lostData;
  579. /***/                        LOG.lostData = 0;
  580. /***/                    }
  581. /***/                    goto returnThisEntry;
  582.                     }
  583.                 }
  584.             }
  585. /***/        else {
  586. returnThisEntry:
  587. /***/            ENTRY.lostData = LOG.lostData;
  588. /***/            LOG.lostData = 0;
  589. /***/            EnableInterrupts(saveSR);
  590.                 ENTRY.tickCount = tickCount;
  591.                 ENTRY.idCode = idCode;
  592.                 ENTRY.format = format;
  593.                 va_start(argPtr, format);
  594.                 destPtr = ENTRY.data;
  595.                 maxString = sizeof (ENTRY.data);
  596.                 while (maxString > 0) {
  597.                     thisFormat = format & kAuditFormatMask;
  598.                     switch (thisFormat) {
  599.                     case kAuditFormatEnd:
  600.                         goto exitLoop;
  601.                     case kAuditFormatString:
  602.                         theString = va_arg(argPtr, StringPtr);
  603.                         /*
  604.                          * Make sure this is a real string pointer. Otherwise, if
  605.                          * it's bogus, theString[0] will cause an address error.
  606.                          */
  607.                         theString = (StringPtr) StripAddress((Ptr) theString);
  608.                         if (theString == NULL
  609.                          || (unsigned long) theString >= LOG.logicalRAMSize) {
  610.                             short                i;
  611.                             unsigned short        value;
  612.  
  613.                             /*
  614.                              * Bogus string pointer (NULL or otherwise extreme):
  615.                              * store the Hex address in the audit record.
  616.                              */
  617.                             for (i = 10; i > 1; i--) {
  618.                                 value = ((unsigned long) theString) & 0x0F;
  619.                                 *((unsigned long *) &theString) >>= 4;
  620.                                 hexString[i] = (value < 10)
  621.                                             ? value + '0' : value + ('A' - 10);
  622.                             }
  623.                             hexString[1] = '?';
  624.                             hexString[0] = 9;
  625.                             theString = hexString;
  626.                         }
  627.                         StoreString(
  628.                             theString,
  629.                             theString[0],
  630.                             maxString,
  631.                             (StringPtr) destPtr
  632.                         );
  633.                         goto exitLoop;
  634.                     case kAuditFormatLocation:
  635.                         GetCallerName(destPtr, maxString, GetA6());
  636.                         goto exitLoop;
  637.                     default:
  638.                         *destPtr++ = va_arg(argPtr, unsigned long);
  639.                         format >>= kAuditFormatShift;
  640.                         maxString -= sizeof (unsigned long);
  641.                         break;
  642.                     }
  643.                 }
  644. exitLoop:        va_end(nextArg);
  645.                 PutQueueEntry(&LOG.data.queue, entryPtr);
  646.                 if (LOG.PSN.highLongOfPSN != 0
  647.                  || LOG.PSN.lowLongOfPSN != kNoProcess)
  648.                     (void) WakeUpProcess(&LOG.PSN);
  649.             }
  650.         }
  651. #undef DATA
  652. }
  653.  
  654. /*
  655.  * The following functions are normally called by the display application:
  656.  */
  657.  
  658. /*
  659.  * Return a pointer to the log area, or NULL if there is none. Note that we make
  660.  * a few sanity checks to better ensure that the gestaltSelector actually refers
  661.  * to a Audit record.
  662.  */
  663. AuditPtr
  664. GetAuditPtr(
  665.         OSType                    gestaltSelector
  666.     )
  667. {
  668.         auto long                gestaltResponse;
  669.         register AuditPtr        auditPtr;
  670.  
  671.         if (TrapAvailable(_Gestalt) == FALSE
  672.          || Gestalt(gestaltSelector, &gestaltResponse) != noErr
  673.          || gestaltResponse == 0
  674.          || (gestaltResponse & 0x3) != 0)
  675.              auditPtr = NULL;
  676.         else {
  677.             auditPtr = (AuditPtr) gestaltResponse;
  678.             /*
  679.              * Sanity check: make sure the result is really an Audit Record and
  680.              * we can deal with the library release that created it.
  681.              */
  682.             if (((unsigned long *) auditPtr)[-kAuditMagicHeaderSize]
  683.                     != kAuditMagicHeader_0
  684.              || LOG.version.u.low < kAuditEarliestVersion
  685.              || LOG.version.u.high > kAuditLatestVersion
  686.              || LOG.recordSize != kAuditRecordSize)
  687.                 auditPtr = NULL;
  688.         }
  689.         return (auditPtr);
  690. }
  691.  
  692. /*
  693.  * Awaken a specified process. Call by the following sequence:
  694.  *        GetCurrentProcess(&oldPSN);        // process to awaken
  695.  *        WakeUpAudit(auditPtr, &oldPSN);
  696.  *    The previous process is now stored in oldPSN)
  697.  *        ... display the log ...
  698.  *        WakeUpAudit(auditPtr, &oldPSN);    // restore old
  699.  *        ExitToShell();
  700.  */
  701. void
  702. WakeUpAudit(
  703.         register AuditPtr        auditPtr,
  704.         ProcessSerialNumber        *oldPSN
  705.     )
  706. {
  707.         ProcessSerialNumber        previousPSN;
  708.         short                    saveSR;
  709.         long                    gestaltResult;
  710.         OSErr                    status;
  711.         
  712.         if (auditPtr != NULL) {
  713.             status = Gestalt(gestaltOSAttr, &gestaltResult);
  714.             if (status != noErr
  715.              || (gestaltResult & (1 << gestaltLaunchControl)) == 0) {
  716.                 oldPSN->highLongOfPSN = 0;
  717.                 oldPSN->lowLongOfPSN = kNoProcess;
  718.             }
  719. /***/        saveSR = DisableInterrupts();
  720. /***/        previousPSN = LOG.PSN;
  721. /***/        LOG.PSN = *oldPSN;
  722. /***/        EnableInterrupts(saveSR);
  723.             *oldPSN = previousPSN;
  724.         }
  725. }
  726.  
  727. /*
  728.  * Enable/disable logging. Returns the old logging state.
  729.  */
  730. Boolean
  731. EnableAudit(
  732.         register AuditPtr        auditPtr,
  733.         Boolean                    enableLogging
  734.     )
  735. {
  736.         Boolean                    oldLogState;
  737.         short                    saveSR;
  738.         
  739.         if (auditPtr == NULL)
  740.             oldLogState = FALSE;
  741.         else {
  742. /***/        saveSR = DisableInterrupts();
  743. /***/        oldLogState = (LOG.flags & kAuditEnabledMask) != 0;
  744. /***/        LOG.flags &= ~kAuditEnabledMask;
  745. /***/        if (enableLogging)
  746. /***/            LOG.flags |= kAuditEnabledMask;
  747. /***/        EnableInterrupts(saveSR);
  748.         }
  749.         return (oldLogState);
  750. }
  751.  
  752. /*
  753.  * Return the value of the Audit enable flag. Returns FALSE if auditPtr is NULL
  754.  * or auditing is disabled.
  755.  */
  756. Boolean
  757. IsAuditEnabled(
  758.         AuditPtr                auditPtr
  759.     )
  760. {
  761.         if (auditPtr == NULL)
  762.             return (FALSE);
  763.         else {
  764.             return ((LOG.flags & kAuditEnabledMask) != 0);
  765.         }
  766. }
  767.  
  768. /*
  769.  * Enable/disable logging. Returns the old logging state.
  770.  */
  771. Boolean
  772. PreserveAudit(
  773.         register AuditPtr        auditPtr,
  774.         Boolean                    preserveFirst
  775.     )
  776. {
  777.         Boolean                    oldPreserveState;
  778.         short                    saveSR;
  779.         
  780.         if (auditPtr == NULL)
  781.             oldPreserveState = FALSE;
  782.         else {
  783. /***/        saveSR = DisableInterrupts();
  784. /***/        oldPreserveState = (LOG.flags & kAuditPreserveFirstMask) != 0;
  785. /***/        LOG.flags &= ~kAuditPreserveFirstMask;
  786. /***/        if (preserveFirst)
  787. /***/            LOG.flags |= kAuditPreserveFirstMask;
  788. /***/        EnableInterrupts(saveSR);
  789.         }
  790.         return (oldPreserveState);
  791. }
  792.  
  793. /*
  794.  * Get the time that the log record was created. This is used to time-stamp log
  795.  * entries. Since the time-stamp is created only when the log is created (and
  796.  * before it is visible through Gestalt), we don't need to lock out interrupts.
  797.  */
  798. void
  799. GetAuditStartTimes(
  800.         register AuditPtr        auditPtr,
  801.         unsigned long            *timeAtStart,
  802.         unsigned long            *ticksAtStart
  803.     )
  804. {
  805.         if (auditPtr == NULL) {
  806.             *timeAtStart = 0;
  807.             *ticksAtStart = 0;
  808.         }
  809.         else {
  810.             *timeAtStart = LOG.timeAtStart;
  811.             *ticksAtStart = LOG.ticksAtStart;
  812.         }
  813. }
  814.  
  815. /*
  816.  * Read the next log entry. This returns a copy of the log entry, if one is
  817.  * available and returns TRUE. This function manages all log queues. If no entry
  818.  * is available, or logging is disabled, the function returns FALSE.
  819.  */
  820. Boolean
  821. ReadAudit(
  822.         register AuditPtr        auditPtr,
  823.         AuditEntryPtr            thisAuditEntry
  824.     )
  825. {
  826.         register AuditQueueEntryPtr    entryPtr;
  827.         
  828.         if (auditPtr == NULL)
  829.             return (FALSE);
  830.         else {
  831.             entryPtr = GetQueueEntry(&LOG.data.queue);
  832.             if (entryPtr == NULL)
  833.                 return (FALSE);
  834.             else {
  835.                 *thisAuditEntry = ENTRY;
  836.                 PutQueueEntry(&LOG.free.queue, entryPtr);
  837.                 return (TRUE);
  838.             }
  839.         }
  840. }
  841.  
  842. /*
  843.  * Store a user-controlled reference value. This may be coerced to any scalar
  844.  * value (such as a memory pointer or longword). SetAuditRefNum returns the
  845.  * current value of the refNum, or zero if auditPtr is NULL.
  846.  */
  847. void
  848. *SetAuditRefNum(
  849.         AuditPtr                auditPtr,
  850.         void                    *refNum
  851.     )
  852. {
  853.         void                *result;
  854.         short                    saveSR;
  855.         
  856.         if (auditPtr == NULL)
  857.             result = NULL;
  858.         else {
  859. /***/        saveSR = DisableInterrupts();
  860. /***/        result = LOG.refNum;
  861. /***/        LOG.refNum = refNum;
  862. /***/        EnableInterrupts(saveSR);
  863.         }
  864.         return (result);
  865. }
  866.  
  867. /*
  868.  * Return the current user-controlled reference value. This may be coerced to any
  869.  * scalar value (such as a memory pointer or longword). This returns zero if
  870.  * auditPtr is NULL or no value had been stored.
  871.  */
  872. void *
  873. GetAuditRefNum(
  874.         AuditPtr                auditPtr
  875.     )
  876. {
  877.         /*
  878.          * Note that we assume that we can retrieve LOG.refNum without
  879.          * interference from interrupt routines. Is this reasonable?
  880.          */
  881.         return ((auditPtr == NULL) ? NULL : LOG.refNum);
  882. }
  883.  
  884. /*
  885.  * Remove the first entry from the queue, return NULL on failure.
  886.  */
  887. static AuditQueueEntryPtr
  888. GetQueueEntry(
  889.         QHdrPtr                    theQueue
  890.     )
  891. {
  892.         register QElemPtr        qElemPtr;
  893.         
  894.         if ((qElemPtr = theQueue->qHead) != NULL) {
  895.             if (Dequeue(qElemPtr, theQueue) != noErr)
  896.                 qElemPtr = NULL;
  897.         }
  898.         return ((AuditQueueEntryPtr) qElemPtr);
  899. }
  900.  
  901. /*
  902.  * TrapAvailable (see Inside Mac VI 3-8)
  903.  */
  904. #define NumToolboxTraps() (                                \
  905.         (NGetTrapAddress(_InitGraf, ToolTrap)            \
  906.                 == NGetTrapAddress(0xAA6E, ToolTrap))    \
  907.             ? 0x200 : 0x400                                \
  908.     )
  909. #define GetTrapType(theTrap) (                            \
  910.         ((theTrap) & 0x800 != 0) ? ToolTrap : OSTrap    \
  911.     )
  912.  
  913. static Boolean
  914. TrapAvailable(
  915.         short                    theTrap
  916.     )
  917. {
  918.         TrapType                trapType;
  919.         
  920.         trapType = GetTrapType(theTrap);
  921.         if (trapType == ToolTrap) {
  922.             theTrap &= 0x07FF;
  923.             if (theTrap >= NumToolboxTraps())
  924.                 theTrap = _Unimplemented;
  925.         }
  926.         return (
  927.             NGetTrapAddress(theTrap, trapType)
  928.             != NGetTrapAddress(_Unimplemented, ToolTrap)
  929.         );
  930. }
  931.  
  932. /*
  933.  * GetCallerName peeks up the calling chain list to the Audit function caller. It
  934.  * then scans through the function code to locate the start and end of the
  935.  * function. The end of the function is followed by the MacsBug name (maybe).
  936.  * This is based on ShowStackChain.c by Greg Anderson greggor@apple.com
  937.  */
  938. static void
  939. GetCallerName(
  940.         unsigned long            *destPtr,
  941.         unsigned short            maxString,
  942.         unsigned long            *a6
  943.     )
  944. {
  945.         unsigned long                returnAddress;
  946.         register unsigned short        *pc;
  947.         short                        maxSearch;
  948.         unsigned short                length;
  949.         Str31                        tempString;
  950.         unsigned long                pcOffset;
  951. #define pcByte        ((unsigned char *) pc)
  952.  
  953.         returnAddress = a6[1];            /* Return address to Audit caller    */
  954.         /*
  955.          * First, look forwards for the symbol name
  956.          */
  957.         pc = (unsigned short *) returnAddress;
  958.         tempString[0] = 0;
  959.         for (maxSearch = 0; maxSearch < 0x4000; maxSearch++, pc++) {
  960.             if (pc[0] == 0x4E56)        /* link                                */
  961.                 break;                    /* We've gone too far                */
  962.             if (pc[0] == 0x4E75            /* rts                                */
  963.               || pc[0] == 0x4ED0        /* jmp (a0)                            */
  964.               || pc[0] == 0x4E74) {        /* rtd #N                            */
  965.                   if (pc[0] == 0x4E74)    /* If rtd, skip over displacement    */
  966.                     pc++;
  967.                 pc++;                    /* Skip over return instruction        */
  968.                  if (pc[0] != 0x4E56) {    /* Link instruction means no name    */
  969.                      /*
  970.                       * pc now points to the word following the RTS. Use the
  971.                       * MacsBug naming conventions to determine the function name,
  972.                       * if any. See Appendix D in the MacsBug User's Guide.
  973.                       */
  974.                       length = pcByte[0] & 0x7F;
  975.                       if (length >= 0x20 && length <= 0x7F) {
  976.                          /*
  977.                           * Fixed length format: first byte is in the range 0x20
  978.                           * through 0x7F, the high bit may or may not be set.
  979.                           */
  980.                           if ((pcByte[1] & 0x80) != 0) {
  981.                               /*
  982.                                * Pascal 16-byte class name format. The string is
  983.                                * stored "method" "class" (each takes 8 bytes) --
  984.                                * MacsBug swaps the order and inserts a '.'
  985.                                * (Warning, this is untested.)
  986.                                */
  987.                               BlockMove(&pcByte[0], &tempString[10], 8);
  988.                               BlockMove(&pcByte[7], &tempString[1], 8);
  989.                               tempString[10] &= ~0x80;
  990.                               tempString[11] &= ~0x80;
  991.                               tempString[9] = '.';
  992.                               tempString[0] = 17;
  993.                           }
  994.                           else {
  995.                               /*
  996.                                * Pascal 8 byte format.
  997.                                */
  998.                               BlockMove(&pcByte[0], &tempString[1], 8);
  999.                               tempString[0] = 7;
  1000.                           }
  1001.                     }
  1002.                     else if (pcByte[0] >= 0x80 && pcByte[0] <= 0x9F) {
  1003.                         /*
  1004.                          * Variable-length string format. If the length byte is
  1005.                          * zero after removing the flag bit, the next byte has
  1006.                          * the true (Str255) length.
  1007.                          */
  1008.                         if (length == 0) {
  1009.                             /*
  1010.                              * Step over the flag. Note that pc and pcByte are the
  1011.                              * same variable (pc is a "short *" while pcByte is a
  1012.                              * "char *"). The next statement adds 1 to pc in a way
  1013.                              * that doesn't cause ANSI compilers to lose their
  1014.                              * electronic lunches.
  1015.                              */
  1016.                             pc = (unsigned short *) &pcByte[1];
  1017.                             length = pcByte[0];
  1018.                         }
  1019.                         StoreString(
  1020.                             pcByte,
  1021.                             length,
  1022.                             sizeof (tempString) - 1,
  1023.                             tempString
  1024.                         );
  1025.                     }
  1026.                     else {
  1027.                         /*
  1028.                          * Something isn't understandable. Do nothing. This will
  1029.                          * be caught by the "if there is no symbol" test below.
  1030.                          */
  1031.                     }
  1032.                 }
  1033.                 break;
  1034.             }
  1035.         }
  1036.         /*
  1037.          * If there is no symbol name, don't search for an offset.
  1038.          */
  1039.         pcOffset = 0;
  1040.         if (tempString[0] != 0) {
  1041.             pc = (unsigned short *) returnAddress;
  1042.             for (maxSearch = 0; maxSearch < 0x4000; maxSearch++) {
  1043.                 if (pc[0] == 0x4E56) {                /* link instruction    */
  1044.                     pcOffset =
  1045.                         ((unsigned long) returnAddress)
  1046.                         - ((unsigned long) pc);
  1047.                     break;
  1048.                 }
  1049.                 --pc;
  1050.             }
  1051.         }
  1052.         if (pcOffset == 0 || tempString[0] == 0) {
  1053.             /*
  1054.              * Since we don't have a MacsBug name, set the offset value to zero
  1055.              * (this will act as a signal) and store  the return address in the
  1056.              * second data value.
  1057.              */
  1058.             if (maxString >= sizeof (unsigned long)) {
  1059.                 *destPtr++ = 0;
  1060.                 maxString -= sizeof (unsigned long);
  1061.             }
  1062.             if (maxString >= sizeof (unsigned long)) {
  1063.                 *destPtr++ = returnAddress;
  1064.                 maxString -= sizeof (unsigned long);
  1065.             }
  1066.         }
  1067.         else {
  1068.             if (maxString >= sizeof (unsigned long)) {
  1069.                 *destPtr++ = pcOffset;
  1070.                 maxString -= sizeof (unsigned long);
  1071.                 StoreString(
  1072.                     tempString,
  1073.                     tempString[0],
  1074.                     maxString,
  1075.                     (StringPtr) destPtr
  1076.                 );
  1077.             }
  1078.         }
  1079. #undef pcByte
  1080. }
  1081.  
  1082. /*
  1083.  * Copy the source string to the destination, respecting the maximum size.
  1084.  */
  1085. static void
  1086. StoreString(
  1087.         const StringPtr        theString,
  1088.         unsigned short        stringLength,
  1089.         unsigned short        maxLength,
  1090.         StringPtr            result
  1091.     )
  1092. {
  1093.         if (stringLength >= maxLength)
  1094.             stringLength = maxLength - 1;
  1095.         BlockMove(theString, result, stringLength + 1);
  1096.         result[0] = stringLength;
  1097. }
  1098.             
  1099.  
  1100. #endif    // SUPPORT_AUDIT